﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects.DataClasses;
using Core.Model;
using Core.UnitOfWork;
using System.Data.Objects;
using System.Data;
using System.Collections;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

namespace Core.Repository
{
    public class Repository<TEntity> : IRepository<TEntity>
         where TEntity : class,IKey<int>
    {
        public Repository(IUnitOfWork unitOfWork)
        {
            this.UnitOfWork = unitOfWork;
        }

        private IUnitOfWork unitOfWork;
        public IUnitOfWork UnitOfWork
        {
            get
            {
                return this.unitOfWork;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException();
                this.unitOfWork = value;
            }
        }
        private DbSet<TEntity> dbSet;
        protected DbSet<TEntity> DbSet
        {
            get
            {
                if (dbSet == null)
                {
                    dbSet = UnitOfWork.Context.Set<TEntity>();
                }
                return dbSet;
            }
        }

        #region IRepository 成员

        public virtual TEntity Get(object id)
        {
            EntityKey key = new EntityKey();

            object obj = UnitOfWork.Context.Set<TEntity>().Find(id);

            if (obj == null)
                return null;
            return obj as TEntity;
        }


        public virtual void Add(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException();

            DbSet.Add(entity);
        }

        public virtual void Update(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException();

            var context = ((IObjectContextAdapter)UnitOfWork.Context).ObjectContext;

            DbEntityEntry entry = UnitOfWork.Context.Entry<TEntity>(entity);


            if (entry.State == EntityState.Modified)
            {
                if (entry.Entity != entity)
                {
                    context.ApplyCurrentValues(typeof(TEntity).Name, entity);
                }
            }
            else
            {
                object currentEntityInDb = DbSet.Find(entity.Id);
                if (currentEntityInDb != null)
                {
                    context.ApplyCurrentValues(typeof(TEntity).Name, entity);
                }
                else
                {
                    throw new ObjectNotFoundException("object not exist !");
                }
            }

        }

        public virtual void Delete(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException();

            var context = ((IObjectContextAdapter)UnitOfWork.Context).ObjectContext;

            DbEntityEntry<TEntity> entry = UnitOfWork.Context.Entry<TEntity>(entity);

            if (entry.State != EntityState.Detached)
            {
                DbSet.Remove((entry.Entity as TEntity));
            }
            else
            {
                object currentEntityInDb = DbSet.Find(entity.Id);
                if (currentEntityInDb != null)
                {
                    DbSet.Remove((TEntity)currentEntityInDb);
                }
                else
                {
                    throw new ObjectNotFoundException("object not exist !");
                }
            }
        }

        public virtual IList<TEntity> Find(Func<TEntity, bool> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            return DbSet.Where(expression).ToList();

        }

        public virtual IList<TEntity> Find<Tkey>(Func<TEntity, bool> expression,
            Func<TEntity, Tkey>[] orderby, bool[] isDescending, int pageIndex, int pageCount)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");
            if (orderby == null)
                throw new ArgumentNullException("orderby");

            if (orderby.Length != isDescending.Length)
                throw new ArgumentException("orderby count not equal isDescending count!");

            var q = DbSet.Where(expression);

            var orderQ = isDescending[0] ? q.OrderByDescending(orderby[0]) : q.OrderBy(orderby[0]);
            for (int i = 1; i < orderby.Length; i++)
            {
                orderQ = isDescending[i] ? orderQ.ThenByDescending(orderby[i]) : orderQ.ThenBy(orderby[i]);
            }
            return orderQ.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList();
        }

        public virtual IList<TEntity> Find<Tkey>(Func<TEntity, bool> expression,
           Func<TEntity, Tkey>[] orderby, bool[] isDescending, int pageIndex, int pageCount, out int pages)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");
            if (orderby == null)
                throw new ArgumentNullException("orderby");

            if (orderby.Length != isDescending.Length)
                throw new ArgumentException("orderby count not equal isDescending count!");

            var q = DbSet.Where(expression);

            pages = (q.Count() + pageCount - 1) / pageCount;

            var orderQ = isDescending[0] ? q.OrderByDescending(orderby[0]) : q.OrderBy(orderby[0]);
            for (int i = 1; i < orderby.Length; i++)
            {
                orderQ = isDescending[i] ? orderQ.ThenByDescending(orderby[i]) : orderQ.ThenBy(orderby[i]);
            }
            return orderQ.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList();
        }

        public virtual IList<TEntity> Find<Tkey>(Func<TEntity, bool> expression,
           Func<TEntity, Tkey>[] orderby, bool[] isDescending, int pageIndex, int pageCount, out int pages, out int records)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");
            if (orderby == null)
                throw new ArgumentNullException("orderby");

            if (orderby.Length != isDescending.Length)
                throw new ArgumentException("orderby count not equal isDescending count!");

            var q = DbSet.Where(expression);
            records = q.Count();
            pages = (records + pageCount - 1) / pageCount;

            var orderQ = isDescending[0] ? q.OrderByDescending(orderby[0]) : q.OrderBy(orderby[0]);
            for (int i = 1; i < orderby.Length; i++)
            {
                orderQ = isDescending[i] ? orderQ.ThenByDescending(orderby[i]) : orderQ.ThenBy(orderby[i]);
            }
            return orderQ.Skip((pageIndex - 1) * pageCount).Take(pageCount).ToList();
        }

        public virtual IList<TEntity> FindAll()
        {
            return DbSet.ToList();


        }
        #endregion
    }
}
